/**
 * 判断一个位置是否在房间入口处（是否骑墙）
 */
export const onEdge = function (pos: RoomPosition): boolean {
    return pos.x === 0 || pos.x === 49 || pos.y === 0 || pos.y === 49
}

/**
 * 判断位置是否越界
 */
export const outOfRange = function (x: number, y: number): boolean {
    return x < 0 || x > 49 || y < 0 || y > 49
}

/**
 * 获取指定方向的相反方向
 *
 * @param direction 目标方向
 */
export const getOppositeDirection = function (direction: DirectionConstant): DirectionConstant {
    return (((direction + 3) % 8) + 1) as DirectionConstant
}

/**
 * 将指定位置序列化为字符串
 * 形如: 12/32/E1N2
 *
 * @param pos 要进行压缩的位置
 */
export const serializePos = function (pos: RoomPosition): string {
    return `${pos.x}/${pos.y}/${pos.roomName}`
}

/**
 * 将位置序列化字符串转换为位置
 * 位置序列化字符串形如: 12/32/E1N2
 *
 * @param posStr 要进行转换的字符串
 */
export const unserializePos = function (posStr: string): RoomPosition | undefined {
    // 形如 ["12", "32", "E1N2"]
    const [x, y, roomName] = posStr.split('/')

    if (!roomName) return undefined
    return new RoomPosition(Number(x), Number(y), roomName)
}

/**
 * 获取基准位置在目标方向上的位置
 *
 * @param origin 基准位置
 * @param direction 目标方向
 */
export const directionToPos = function (origin: RoomPosition, direction: DirectionConstant): RoomPosition | undefined {
    let targetX = origin.x
    let targetY = origin.y

    // 纵轴移动，方向朝下就 y ++，否则就 y --
    if (direction !== LEFT && direction !== RIGHT) {
        if (direction > LEFT || direction < RIGHT) targetY--
        else targetY++
    }
    // 横轴移动，方向朝右就 x ++，否则就 x --
    if (direction !== TOP && direction !== BOTTOM) {
        if (direction < BOTTOM) targetX++
        else targetX--
    }

    // 如果要移动到另一个房间的话就返回空，否则返回目标 pos
    if (targetX < 0 || targetY > 49 || targetX > 49 || targetY < 0) return undefined
    else return new RoomPosition(targetX, targetY, origin.roomName)
}

export interface GetFreeSpaceOpts {
    // 是否忽略 creep，默认 true
    ignoreCreep?: boolean
    range?: number
    direction?: TOP | RIGHT | BOTTOM | LEFT
}

/**
 * 获取目标位置周围的开采位空位
 * 也会搜索建筑、工地
 */
export const getFreeSpace = function (origin: RoomPosition, opts: GetFreeSpaceOpts = {}): RoomPosition[] {
    const range = opts.range || 1
    const ignoreCreep = typeof opts.ignoreCreep === 'undefined' ? true : opts.ignoreCreep
    const direction = opts.direction || TOP

    if (ignoreCreep && origin._freeSpaceIgnoreCreep) return origin._freeSpaceIgnoreCreep
    if (!ignoreCreep && origin._freeSpace) return origin._freeSpace

    const terrain = new Room.Terrain(origin.roomName)
    let result: RoomPosition[] = []

    const addPosition = (x: number, y: number) => {
        if (x < 0 || x > 49 || y < 0 || y > 49) return
        if (x === origin.x && y === origin.y) return
        if (terrain.get(x, y) === TERRAIN_MASK_WALL) return

        result.push(new RoomPosition(x, y, origin.roomName))
    }

    if (direction === TOP || direction === BOTTOM) {
        const yRange = direction === TOP ? range : -range
        for (let y = origin.y - yRange; y <= origin.y + yRange; y++) {
            for (let x = origin.x - range; x <= origin.x + range; x++) {
                addPosition(x, y)
            }
        }
    } else {
        const xRange = direction === LEFT ? range : -range
        for (let x = origin.x - xRange; x <= origin.x + xRange; x++) {
            for (let y = origin.y - range; y <= origin.y + range; y++) {
                addPosition(x, y)
            }
        }
    }

    // 附近会占据位置的游戏对象
    const nearGameObject: (Structure | ConstructionSite | Creep)[] = [
        ...origin.findInRange(FIND_STRUCTURES, range),
        ...origin.findInRange(FIND_CONSTRUCTION_SITES, range),
    ].filter(
        ({ structureType }) =>
            structureType !== STRUCTURE_CONTAINER &&
            structureType !== STRUCTURE_RAMPART &&
            structureType !== STRUCTURE_ROAD,
    )

    if (!ignoreCreep) {
        nearGameObject.push(...origin.findInRange(FIND_CREEPS, range))
    }

    const nearGameObjectPos = new Set(nearGameObject.map((obj) => `${obj.pos.x}/${obj.pos.y}`))

    // 筛掉会占据位置的对象
    result = result.filter((pos) => !nearGameObjectPos.has(`${pos.x}/${pos.y}`))
    if (ignoreCreep) return (origin._freeSpaceIgnoreCreep = result)
    else return (origin._freeSpace = result)
}

/**
 * 判断某个位置是否是空位
 */
export const isFreePos = function (pos: RoomPosition, ignoreCreep = true): boolean {
    const terrain = pos.lookFor(LOOK_TERRAIN)[0]
    if (terrain === 'wall') return false

    const structures = pos.lookFor(LOOK_STRUCTURES)
    if (
        structures.find(
            ({ structureType }) =>
                structureType !== STRUCTURE_CONTAINER &&
                structureType !== STRUCTURE_RAMPART &&
                structureType !== STRUCTURE_ROAD,
        )
    ) {
        return false
    }

    const constructionSites = pos.lookFor(LOOK_CONSTRUCTION_SITES)
    if (
        constructionSites.find(
            ({ structureType }) =>
                structureType !== STRUCTURE_CONTAINER &&
                structureType !== STRUCTURE_RAMPART &&
                structureType !== STRUCTURE_ROAD,
        )
    ) {
        return false
    }

    if (!ignoreCreep && pos.lookFor(LOOK_CREEPS).length) return false

    return true
}

/**
 * 判断爬是否在房间边缘附近
 */
export const isNearEdge = function (pos: RoomPosition, range = 1): boolean {
    return pos.x <= range || pos.x >= 49 - range || pos.y <= range || pos.y >= 49 - range
}

/**
 * 找到最近的对象
 */
export const findClosestObjByXY = function <T extends RoomObject>(x: number, y: number, objects: T[]): T | null {
    let minRange = Infinity
    let result: T | null = null

    for (const object of objects) {
        const range = Math.pow(object.pos.x - x, 2) + Math.pow(object.pos.y - y, 2)
        if (range < minRange) {
            minRange = range
            result = object
        }
    }

    return result
}

/**
 * 找到最近的位置
 */
export const findClosestPosByXY = function (x: number, y: number, posArray: RoomPosition[]): RoomPosition | null {
    let minRange = Infinity
    let result: RoomPosition | null = null

    for (const pos of posArray) {
        const range = Math.pow(pos.x - x, 2) + Math.pow(pos.y - y, 2)
        if (range < minRange) {
            minRange = range
            result = pos
        }
    }

    return result
}

/**
 * 寻找 storage 和 terminal 共同相邻的位置
 */
export const findStorageAndTerminalBothNearPos = function (room: Room): RoomPosition[] {
    const storage = room.storage
    const terminal = room.terminal

    if (!storage || !terminal) return []

    // 取 storage 和 terminal 之间的中点
    const midX = (storage.pos.x + terminal.pos.x) / 2
    const midY = (storage.pos.y + terminal.pos.y) / 2

    // 生成中点周围的位置，不会超过4个，实际最多2个
    const result = []
    for (let x = Math.ceil(midX - 0.5); x <= Math.floor(midX + 0.5); x++) {
        for (let y = Math.ceil(midY - 0.5); y <= Math.floor(midY + 0.5); y++) {
            if (x === storage.pos.x && y === storage.pos.y) continue
            if (x === terminal.pos.x && y === terminal.pos.y) continue
            result.push(new RoomPosition(x, y, room.name))
        }
    }

    return result.filter((pos) => isFreePos(pos))
}

declare global {
    interface RoomPosition {
        /** 本 tick 的周围空余开采位缓存 */
        _freeSpace: RoomPosition[]
        _freeSpaceIgnoreCreep: RoomPosition[]
        /** 到目标点的距离 */
        _dis: number
    }
}
